////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    SaturnGPGuts.cpp											  //
//  Developer:																  //	
//  Create date:                                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Saturn GP					  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        2012 - Riplife Gaming.  All rights reserved.						  //
////////////////////////////////////////////////////////////////////////////////
#include "SaturnGPGuts.h"
#include "slotconn.h"
#include "sltsql.h"

////////////////////////////////////////////////////////////////////////////
/// SaturnGPGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 aces, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SpinResult SaturnGPGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1			2				3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_FIVE_WILD		},
/* ICON_SATURN */		{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_SATURN,	EVT_THREE_SATURN,	EVT_FOUR_SATURN,	EVT_FIVE_SATURN		},
/* ICON_FLAG */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_FLAG,	EVT_THREE_FLAG,		EVT_FOUR_FLAG,		EVT_FIVE_FLAG		},
/* ICON_RACER */		{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_RACER,	EVT_THREE_RACER,	EVT_FOUR_RACER,		EVT_FIVE_RACER		},
/* ICON_GIRL */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_GIRL,		EVT_FOUR_GIRL,		EVT_FIVE_GIRL		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_ACE,		EVT_FOUR_ACE,		EVT_FIVE_ACE		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_KING,		EVT_FOUR_KING,		EVT_FIVE_KING		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_QUEEN,	EVT_FOUR_QUEEN,		EVT_FIVE_QUEEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_JACK,		EVT_FOUR_JACK,		EVT_FIVE_JACK		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_THREE_TEN,		EVT_FOUR_TEN,		EVT_FIVE_TEN		},
/* ICON_BONUS */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER			}
};


////////////////////////////////////////////////////////////////////////////
/// SaturnGPGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Scatter, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_SCATTER ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_SCATTER.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SpinResult SaturnGPGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1			2						3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_FIVE_WILD		},
/* ICON_SATURN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_FLAG */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_RACER */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_GIRL */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_BONUS */		{	EVT_LOSER,	EVT_LOSER,	EVT_BONUS_NEAR_MISS,	EVT_THREE_BONUS,	EVT_FOUR_BONUS,		EVT_FIVE_BONUS		},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_THREE_SCATTER,	EVT_FOUR_SCATTER,	EVT_FIVE_SCATTER	}
};


////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::SaturnGPGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SaturnGPGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::~SaturnGPGuts()
///
/// Destructor for SaturnGPGuts. 
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::~SaturnGPGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::SpinResult SaturnGPGuts::getWildLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Wild line win payout.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SpinResult SaturnGPGuts::getWildLineWinResult( uint8* stops, uint8 line )
{
	// Count how many Wilds we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Wild...
		int32 iIconID = getIcon( getGameModeId(), i, stops[i] )->iconid;
		if ( iIconID != ICON_WILD )
		{
			break;	
		}
    }

	// Use the array to look up the spin result for i wilds.
	return lookupLineWinSpinResult( ICON_WILD, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::SpinResult SaturnGPGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SpinResult SaturnGPGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Wild
		if ( currentIcon == ICON_WILD )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		} 
		else if ( testIcon == ICON_WILD )
		{
			// Otherwise if the test icon is a Wild...
			// then we still have a substitution.  In this case the line
			// started out with a Wild.
			bSubstitution = true;

			// We have now found our first icon that is not a Wild.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			break;											
        }
    }

	// If we made it all the way through and the testIcon is still a wild...
	if ( testIcon == ICON_WILD )
	{
		// then this must be a 5 wild win, so reset the substitution flag.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
  	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool SaturnGPGuts::doesWildLineWinPayMore( SpinResult srWild, SpinResult srOther ) const
///
/// @param   srWild		The spin result to calculate the Wild payout for.
/// @param   srOther		The spin result to calculate the other payout for.
///
/// @return	 bool			True if the Wild win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool SaturnGPGuts::doesWildLineWinPayMore( SpinResult srWild, SpinResult srOther ) const
{
	// Lookup the payout for the Wild line win.
	long nWildLineWinPayout = getPayout( srWild )->payoutAmount;

	// Lookup the payout for the other line win.
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// Return true if the Wild payout is bigger.
    return ( nWildLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// No multiplier when wild substitution in Saturn GP
	multiplier = 1;

	// Track the Wild result separately since it might be changed to a substitution.
	SpinResult srWildLineWinResult = getWildLineWinResult( stops, line );  

	// Now see what the payout is if we use any Wild we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Wild as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution );

	// Use the largest payout between the regular Wild evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesWildLineWinPayMore( srWildLineWinResult, srResultWithPossibleSubstitution ) )
	{
		// The Wilds by themselves pay out more so use the Wild result.
		rtnresult = srWildLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;
	}

	if ( bSubstitution && (rtnresult != EVT_LOSER) )
	{
		// then add a note.
		spinline->addNote( NOTE_WILD_SUBSTITUTION );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		SaturnGPGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// Jackpot hit?
		int32 nJackpotHit = getRandomValueFromProbabilityTable(PT_RANDOM_JACKPOT);

		if (nJackpotHit || m_bForceRandomJackpot)
		{
			increaseTotalWin( getPayout(EVT_RANDOM_JACKPOT)->payoutAmount );
			
			// then add a jackpot scatter to the spin. For Saturn GP, fixed amount of 10000
			spin->addScatter( 0, getPayout(EVT_RANDOM_JACKPOT)->payoutAmount, 1, true, EVT_RANDOM_JACKPOT, 0 );
		}	
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
SaturnGPGuts::SpinResult SaturnGPGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == testIcon )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		int32 nPayout = 0;
		
		// Get the base payout for the scatter. 
		nPayout = getPayout( srScatterResult )->payoutAmount;
			
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nPayout * getBetPerLine() );
		
		// Add a scatter to the spin.
		spin->addScatter( scatterPositions, nPayout * getBetPerLine(), 1, false, srScatterResult, 0 );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// 1) See if there is a scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_SCATTER, nScatterPositions );

	// Add the scatter, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	//  If three or more scatters, activate trophy bonus round
	if (srResult == EVT_THREE_SCATTER || srResult == EVT_FOUR_SCATTER || srResult == EVT_FIVE_SCATTER)
	{
		// Only if we are in normal mode. Trophy bonus game is NOT retriggered
		if (getGameModeId() == MODE_NORMAL)
		{
			m_subGame->update( SGIDX_BONUS_GAME_STATE, bg_Trophy );
			m_subGame->update( SGDIDX_TROPY_SCATTER_WIN, srResult );
		}
	}
	else
	{
		// 2) See if there is a bonus scatter
		nScatterPositions = 0;
		srResult = getScatterWinResult( spin, ICON_BONUS, nScatterPositions );

		// Add the scatter, if any.
		evaluateScatterWin( spin, srResult, nScatterPositions );

		//  If three or more bonus scatters, activate race or shooting bonus round
		if (srResult == EVT_THREE_BONUS || srResult == EVT_FOUR_BONUS || srResult == EVT_FIVE_BONUS)
		{
			// Only if we are in normal mode. Race and Shooting bonus game are NOT retriggered
			if (getGameModeId() == MODE_NORMAL)
			{
				int32 bg_Type = (getrand() % 2) ? bg_Shooting : bg_Race;	// 50% chance
				m_subGame->update( SGIDX_BONUS_GAME_STATE, bg_Type );
				m_subGame->update( SGDIDX_REMAINING_STEPS, 5 );
				m_subGame->setServerOnly( SGDIDX_REMAINING_STEPS, false );
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 SaturnGPGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
{
    // Just defer to the base class.
	SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::preSpinClassifyResults( SlotSpin* spin )
{
	SlotGuts::preSpinClassifyResults(spin);

	m_subGame->setServerOnly( SGDIDX_TROPY_SCATTER_WIN, true );
	m_subGame->setServerOnly( SGDIDX_REMAINING_STEPS, true );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// If we have triggered a bonus round, game cannot complete
	if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) > 0)
	{
		setGameCanComplete( false );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 SaturnGPGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 HydroHeatGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8 SaturnGPGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	int32 bg_Type = m_subGame->getData(SGIDX_BONUS_GAME_STATE);
	if ( bg_Type == bg_Trophy )
	{
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int32 nScatterWin = m_subGame->getData(SGDIDX_TROPY_SCATTER_WIN);
			int32 nProbatilityTable = PT_TROPHY_3_SCATTER;
			// Which probability table do we have to use? Depends on the scatter win which triggered this bonus
			switch(nScatterWin)
			{
			case EVT_THREE_SCATTER: nProbatilityTable = PT_TROPHY_3_SCATTER;
									break;
			case EVT_FOUR_SCATTER:  nProbatilityTable = PT_TROPHY_4_SCATTER;
									break;
			case EVT_FIVE_SCATTER:  nProbatilityTable = PT_TROPHY_5_SCATTER;
									break;						
			}

			// Get number of free spins won with this bonus
			int32 nFreeSpinsWon = getRandomValueFromProbabilityTable(nProbatilityTable);

			// Add bonus game (no money, just free spins won)
			addBonusGame(bg_Type, 0, 0, 0, 0, nFreeSpinsWon); 
			
			// End bonus, switch to free spins mode
			setGameCanComplete( true );
			incrementFreeSpins( nFreeSpinsWon );
			setGameModeId( MODE_FREE_SPIN );
			m_subGame->update( SGIDX_BONUS_GAME_STATE, bg_NoBonus );
			m_subGame->update( SGDIDX_TROPY_SCATTER_WIN, EVT_LOSER );
			bReply = true;			
		}
	}
	else if ((bg_Type == bg_Shooting) || (bg_Type == bg_Race))
	{
		// Shooting bonus game
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int32 nRemainingSteps = m_subGame->getData(SGDIDX_REMAINING_STEPS);
			int32 nProbabilityTable = (bg_Type == bg_Shooting) ? PT_SHOOTING_STEP_1 : PT_RACE_STEP_1;

			// Which probability table do we have to use? Depends on the step/bonus type we are 
			switch(bg_Type)
			{
				case bg_Shooting:
				{
					switch(nRemainingSteps)
					{
					case 5: nProbabilityTable = PT_SHOOTING_STEP_1;	// Step 1
							break;
					case 4: nProbabilityTable = PT_SHOOTING_STEP_2;	// Step 2
							break;
					case 3: nProbabilityTable = PT_SHOOTING_STEP_3;	// Step 3
							break;
					case 2: nProbabilityTable = PT_SHOOTING_STEP_4;	// Step 4
							break;
					case 1: nProbabilityTable = PT_SHOOTING_STEP_5;	// Step 5
							break;
					}
				}
				break;
				case bg_Race:
				{
					switch(nRemainingSteps)
					{
					case 5: nProbabilityTable = PT_RACE_STEP_1;	// Step 1
							break;
					case 4: nProbabilityTable = PT_RACE_STEP_2;	// Step 2
							break;
					case 3: nProbabilityTable = PT_RACE_STEP_3;	// Step 3
							break;
					case 2: nProbabilityTable = PT_RACE_STEP_4;	// Step 4
							break;
					case 1: nProbabilityTable = PT_RACE_STEP_5;	// Step 5
							break;
					}
				}
			}

			// Prize won in this step
			int32 nPrize = getRandomValueFromProbabilityTable(nProbabilityTable);

			// Multiply win by bet			 
			int32 nBetPerLine = m_subGame->getData(SGIDX_BET_PER_LINE);
			int32 nLinesBet = m_subGame->getData(SGIDX_NUM_LINES_BET);
			int32 nStepBonusWin = nPrize * nBetPerLine * nLinesBet;

			// Add bonus win
			addBonusGameSaturnGP(bg_Type, 5-nRemainingSteps, nBetPerLine*nLinesBet, nPrize, nStepBonusWin, 0); 
			
			// Decrease number of steps
			m_subGame->update(SGDIDX_REMAINING_STEPS, --nRemainingSteps);
			if (nRemainingSteps == 0)
			{
				// If no more steps, finish bonus game
				m_subGame->update( SGIDX_BONUS_GAME_STATE, bg_NoBonus );
				m_subGame->setServerOnly( SGDIDX_REMAINING_STEPS, true );
				setGameCanComplete( true );
			}
			
			bReply = true;		
		}
	}
 	
	return bReply;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 SaturnGPGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void SaturnGPGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=0;
	pStops[1]=0;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

BonusGame*
SaturnGPGuts::addBonusGameSaturnGP(uint8 bonusgametype,uint8 bonusgameindex,int32 basepayoutamount,int32 totalmultiplier,int32 totalbonuspayoutamount,int32 freespins)
{
	increaseTotalWin(totalbonuspayoutamount);
	
	SlotSpin* spin = getSlotSpin();
	return spin->addBonusGame(bonusgametype, bonusgameindex, basepayoutamount, totalmultiplier, totalbonuspayoutamount, 0, 0);

}

int32 SaturnGPGuts::getFixedJackpotValue()
{
	return getPayout( EVT_RANDOM_JACKPOT )->payoutAmount;
}